package cn.felord.security.autoconfigure;


import cn.felord.security.autoconfigure.handler.SimpleAccessDeniedHandler;
import cn.felord.security.autoconfigure.handler.LoginAuthenticationSuccessHandler;
import cn.felord.security.autoconfigure.handler.SimpleAuthenticationEntryPoint;
import cn.felord.security.autoconfigure.jwt.JwtTokenGenerator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationEntryPointFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Collections;
import java.util.Set;
import java.util.function.Function;


/**
 * The Servlet web security configuration.
 *
 * @author felord.cn
 * @since 2021 /3/26 11:35
 */
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class ServletWebSecurityConfiguration {


    /**
     * Role checker role checker.
     *
     * @param function the function
     * @return the role checker
     */
    @Bean
    @ConditionalOnMissingBean
    RoleChecker roleChecker(Function<Set<String>, Set<AntPathRequestMatcher>> function) {
        return new JdbcRoleChecker(function);
    }

    /**
     * Web security filter chain security filter chain.
     *
     * @param httpSecurity                          the http security
     * @param jwtTokenGenerator                     the jwt token generator
     * @param bearerAccessTokenAuthenticationFilter the bearer access token authentication filter
     * @return the security filter chain
     * @throws Exception the exception
     */
    @Bean
    @ConditionalOnMissingBean(name = "defaultWebSecurityFilterChain")
    SecurityFilterChain defaultWebSecurityFilterChain(HttpSecurity httpSecurity, JwtTokenGenerator jwtTokenGenerator, BearerAccessTokenAuthenticationFilter bearerAccessTokenAuthenticationFilter) throws Exception {

        SimpleAuthenticationEntryPoint authenticationEntryPoint = new SimpleAuthenticationEntryPoint();
        httpSecurity.csrf().disable()
                .cors()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .exceptionHandling()
                .accessDeniedHandler(new SimpleAccessDeniedHandler())
                .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                .authorizeRequests().anyRequest().access("@roleChecker.check(authentication,request)")
                .and()
                .addFilterBefore(bearerAccessTokenAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                .successHandler(new LoginAuthenticationSuccessHandler(jwtTokenGenerator))
                .failureHandler(new AuthenticationEntryPointFailureHandler(authenticationEntryPoint));
        return httpSecurity.build();
    }


    /**
     * Swagger web security customizer web security customizer.
     *
     * @return the web security customizer
     */
    @Bean
    WebSecurityCustomizer swaggerWebSecurityCustomizer(){
        return web -> {
            //所需要用到的静态资源，允许访问
            web.ignoring().antMatchers( "/swagger-ui.html",
                    "/swagger-ui/*",
                    "/swagger-resources/**",
                    "/v2/api-docs",
                    "/v3/api-docs",
                    "/webjars/**");

        };
    }

    /**
     * Cors configuration source cors configuration source.
     *
     * @return the cors configuration source
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Collections.singletonList(CorsConfiguration.ALL));
        configuration.setAllowedMethods(Collections.singletonList(CorsConfiguration.ALL));
        configuration.setAllowedHeaders(Collections.singletonList(CorsConfiguration.ALL));
        configuration.setExposedHeaders(Collections.singletonList(CorsConfiguration.ALL));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
